Session 1 Part 2

Let's implement the rest of the circuit simulation. The simulation will solve some differential equations (described below) with a simple timestep loop. Each loop iteration will have three steps:

  1. calculate_new_currents
  2. distribute_charge
  3. update_voltages (provided for your reference)

Your goal for this exercise is to implement the two remaining tasks and finish the simulation loop.

Task calculate_new_currents

This task updates currents and voltages of wire segments. Each wire consists of three segments and can be drawn like this:

$\hskip{8em}I_0\hskip{11.5em}I_1\hskip{11em}I_2$

           current._0                 current._1                current._2

in_node --------------- voltage._1 -------------- voltage._2 ------------- out_node

$\hskip{2.5em}V_0\hskip{10em}V_1\hskip{11em}V_2\hskip{10em}V_3$

On this topology of wire segments, the RLC model you are going to implement is described by the following equation:

$\large I_i^s = \big((V_{i+1}^{s-1} - V_i^{s-1}) - L \frac{I_i^{s-1} - I_i^0}{dT}\big)/R\quad \textrm{for}\quad 0 \le i < 3$

$\large V_{i+1}^s = V_i^0 + \frac{I_i^s - I_{i+1}^s}{C}dT \quad \textrm{for}\quad 0 \le i < 2$

At each time step $s$, the difference in voltages induces currents on each segment and the induced current accumulates to the voltage at each node. The task repeatedly computes new currents and voltages for conf.steps times, and once all steps are done, it should store the final currents and voltages back to the region of wires.

Implement the body of the task based on this description.

Task distribute_charge

This task adjusts the charge of in_node and out_node of each wire. Since the currents flowed from in_node to out_node, charges have been taken from the former and delivered to the latter. The contribution of the currents to the charge is formulated as:

$\large \mathit{dCharge} = \mathit{I} \times \mathit{dT}$

Implement the task body based on this description.

Complete the Simulation Loop

The body of circuit simulation is a timestep loop with three steps (in the following order):

  1. calculate_new_currents
  2. distribute_charge
  3. update_voltages

The loop will run for conf.num_loops timesteps. Finish the loop and run the application to make sure everything is working correctly.

Bonus: Calculate the FLOPS of Your Implementation

Optionally, you can calculate the FLOPS used by your implementation. We've provided code that measures the time elapsed during the simulation. The helper function helper.calculate_gflops takes this time and the FLOPs per iteration of each of the three tasks (which you must provide), and computes the total FLOPS of the simulation.

Analyze your task implementations and calculate the total FLOPS.

Syntax Guide


In [ ]:
task T(A1 : T1, ...) ... end -- Creates a task T with parameters A1 (of type T1), etc.
task T(A1 : T1, ...) where reads(A1) do ... end -- As above, but with read-only privileges on A1.
task T(A1 : T1, ...) where reads writes(A1) do ... end -- As above, but with read-write privileges on A1.
for V in R do ... end -- Loops over each element V in R. (V is a pointer to each element.)

Exercise


In [ ]:
import "regent"

local c = regentlib.c

struct Currents {
  _0 : float,
  _1 : float,
  _2 : float,
}

struct Voltages {
  _1 : float,
  _2 : float,
}

fspace Node {
  capacitance : float,
  leakage     : float,
  charge      : float,
  voltage     : float,
}

fspace Wire(rn : region(Node)) {
  in_node     : ptr(Node, rn),
  out_node    : ptr(Node, rn),
  inductance  : float,
  resistance  : float,
  capacitance : float,
  current     : Currents,
  voltage     : Voltages,
}

local CktConfig = require("session1/circuit_config")
local helper = require("session1/circuit_helper")
local validator = require("session1/circuit_validator")

-- The length of a timestep is 0.1 us.
local dT = 1e-7

-- TODO: Implement task 'calculate_new_currents'.
task calculate_new_currents(conf : CktConfig)
                            -- TODO: Declare more parameters as needed.
-- TODO: Declare privileges.

  -- TODO: Starting with the following code, implement the body of the task.
  --for w in rw do
  --  for s = 1, conf.steps + 1 do
  --  end
  --end
end

-- TODO: Implement task 'distribute_charge'.
task distribute_charge() -- TODO: Declare parameters as needed.
-- TODO: Declare privileges.

  -- TODO: Starting with the following code, implement the body of the task.
  --for w in rw do
  --end
end

-- The 'update_voltages' task accumulates the contribution of charge
-- to voltage and then applies leakage.
task update_voltages(rn : region(Node))
where
  reads(rn.{capacitance, leakage}),
  reads writes(rn.{voltage, charge})
do
  for n in rn do
    var voltage = n.voltage + n.charge / n.capacitance
    voltage = voltage * (1.0 - n.leakage)
    n.voltage = voltage
    n.charge = 0.0
  end
end

task toplevel()
  var conf : CktConfig
  conf:initialize_from_command()
  conf:show()

  -- This is our solution for Session 1 Part 1. Does it match what you wrote?
  var num_circuit_nodes = conf.num_pieces * conf.nodes_per_piece
  var num_circuit_wires = conf.num_pieces * conf.wires_per_piece

  var rn = region(ispace(ptr, num_circuit_nodes), Node)
  var rw = region(ispace(ptr, num_circuit_wires), Wire(rn))

  new(ptr(Node, rn), num_circuit_nodes)
  new(ptr(Wire(rn), rw), num_circuit_wires)

  c.printf("Generating a random circuit...\n")
  helper.generate_random_circuit(rn, rw, conf)
  helper.dump_graph(conf, rn, rw)

  c.printf("Starting main simulation loop\n")
  var ts_start = helper.timestamp()

  -- TODO: Complete the simulation loop by calling the other two tasks.
  for j = 0, conf.num_loops do
    update_voltages(rn)
  end

  -- Wait for all previous tasks to complete and measure the elapsed time.
  helper.wait_for(rn, rw)
  var ts_end = helper.timestamp()
  c.printf("Simulation complete\n")

  var sim_time = 1e-6 * (ts_end - ts_start)
  c.printf("ELAPSED TIME = %7.3f s\n", sim_time)

  -- Bonus: Calculate FLOPS of your implementation.
  var flops_cnc, flops_dc, flops_uv = 0, 0, 0
  var gflops =
    helper.calculate_gflops(sim_time, flops_cnc, flops_dc, flops_uv, conf)
  c.printf("GFLOPS = %7.3f GFLOPS\n", gflops)

  c.printf("Validating simulation results...\n")
  validator.validate_solution(rn, rw, conf)
end
regentlib.start(toplevel)